#用于打开或者关闭在运行时输出Log的功能
OpenLog = False;
OpenEnterFunLog = False

#定义一个求符号函数
def Sign(num):
    if num>=0:
        return 1
    else:
        return -1

#获取输入
#region 
def GetInput():
    #暂时按照教材上的输入
    x1=[3,3];y1=1
    x2=[4,3];y2=1
    x3=[1,1];y3=-1
    InputList = [[x1,y1],[x2,y2],[x3,y3]]
    return InputList

def GetInputD3():
    #自己造点数据 把x的第三个分量的正负和y对应起来，以保证数据是线性可分的
    x1=[3,3,1];y1=1
    x2=[4,3,5];y2=1
    x3=[1,1,-3];y3=-1
    x4=[5,12,-3];y4=-1
    x5=[1,1,5];y5=1
    x6=[1,20,-3];y6=-1
    InputList = [[x1,y1],[x2,y2],[x3,y3],[x4,y4],[x5,y5],[x6,y6]]
    return InputList

def GetInputD3_2():
    #自己造点数据 构想是在三维坐标系中 分别将点放到 两个象限，
    #设置其中一个象限的y值为1，另一个象限的设置为-1，以保证数据线性可分
    x1=[3,3,1];y1=1
    x2=[4,3,5];y2=1
    x3=[1,-50,-3];y3=-1
    x4=[5,-12,-3];y4=-1
    x5=[1,1,5];y5=1
    x6=[1,-20,-3];y6=-1
    InputList = [[x1,y1],[x2,y2],[x3,y3],[x4,y4],[x5,y5],[x6,y6]]
    return InputList
#endregion

#向量加法
#将向量v1和v2相加 返回值是两个向量的和,如果两个向量维度不同，返回False
#例如 v3 = VectorPlus(v1,v2) 则v3处是向量v1,v2的和
def VectorPlus(v1,v2):
    if OpenEnterFunLog:
        print("Enter VectorPlus")   
    l1 = len(v1)    
    l2 = len(v2)
    v3 = []
    if l1==l2:
        i=0
        while(i<l1):
            v3.append(v1[i]+v2[i])
            i=i+1
        return v3
    else:
        print("Can not Calu this two vector because v1 and v2 have different dimension\nv1's dimension is %d \nv2's dimension is %d"%(l1,l2))
        return False
#向量加法使用示范
#region    
#v1 = [1,5,8,6]
#v2 = [5,1,0,10]
#vTest = VectorPlus(v1,v2)
#if vTest:
#    print(vTest)
#endregion

#求两个向量的内积
def VectorInnerProduct(v1,v2):
    if OpenEnterFunLog:
        print("Enter VectorInnerProduct")
    l1 = len(v1)    
    l2 = len(v2)
    value = 0
    if l1==l2:
        i=0
        while(i<l1):
            value = value+v1[i]*v2[i]
            i=i+1
    else:
        value = False
        print("Can not Calu this two vector because v1 and v2 have different dimension\nv1's dimension is %d \nv2's dimension is %d"%(l1,l2))
    return value
#求向量内积使用示范
#region    
#v1 = [1,5,8,6]
##v2 = [5,1,0,10]
#v2 = [5,1,0]
#num = VectorInnerProduct(v1,v2)
#if num:
#    #print("Calc Ok")
#    print(num)
#endregion

#向量乘以一个数
def VectorMulNum(v1,num):
    if OpenEnterFunLog:
        print("Enter VectorMulNum")
    i=0;
    l1 = len(v1)
    v3 = []
    while(i<l1):
        v3.append(v1[i]*num)
        i = i+1
    return v3

#开始学习
# 0<lrate<=1 lrate作为步长需要大于0，但也不宜过大
#StartLearn Start
def StartLearn(data,w=[],b=0,lrate=1):
    count = 0;#计数迭代的轮数
    print(data)#输出测试用数据

    #Init w Start
    i = 0
    while(i<len(data[0][0])):
        w.append(0)
        i=i+1
    #Init w Done

    #从数据集中选取一个点
    haveFailPoint = True;           #标记是否有预测失败的点
    dataNumMax = len(data)         #数据集的个数

    #如果有预测失败的点就继续循环
    while(haveFailPoint):
        count =count+1
        print("开始第%d轮迭代"%(count))
        haveFailPoint = False
        i=0;                            #遍历数据集中的点
        ShowWAndB(w,b)

        while(i<dataNumMax):
            checkResult = data[i][1]*Sign(VectorInnerProduct(w,data[i][0])+b)
            print("检测点为:%d"%(i),end = "")
            print("其值为：",end = "")
            print(data[i],end = "")
            print("检测结果为：%d"%(checkResult))

            if checkResult==1:#检验结果正确继续选取下一个点
                i=i+1
            else:#检验结果错误，更新w和b的值，重新开始数据集的遍历
                w = VectorPlus(w,VectorMulNum(data[i][0],lrate*data[i][1]))
                b = b + lrate*data[i][1]
                haveFailPoint = True
                print("跳出本轮迭代，更新w和b的值，更新后：")
                ShowWAndB(w,b) #输出w和b的值
                print("-----------------------------------")
                break
    
    print("-----------------------------------")
    print("共迭代%d轮"%(count))#输出迭代的总轮数
    print("求得超平面，系数如下：")
    ShowWAndB(w,b)
#StartLearn End

#格式化输出W和B的值
def ShowWAndB(w,b):
    print("w:",end = "")
    print(w,end="")
    print(" b:",end = "")
    print(b)

#开始测试
#StartLearn(GetInput())
#StartLearn(GetInputD3(),[],100,1)
#StartLearn(GetInputD3(),[],0,1)
#StartLearn(GetInputD3(),[],0,0.5)
#StartLearn(GetInputD3(),[],0,100)
#StartLearn(GetInputD3_2(),[],0,100)


#感知机的对偶形式
#参数data为训练用数据，lrate为步长，α和b默认为0
#PerceptronV2 Start 
def PerceptronV2(data,lrate=1):
    count = 0;#计数迭代的轮数
    print(data)#输出测试用数据
    b=0#初始b为0
    dimention = len(data[0][0])
    dataNum = len(data) #这是data中数据的个数，也是Gram矩阵的阶数
    #计算Gram矩阵
    gram = []
    row = 0
    #因为矩阵是对称的所以算出来一半就行了
    while(row<dataNum):
        temp = []
        column = 0
        while(column<=row):
            temp.append(VectorInnerProduct(data[row][0],data[column][0]))
            column = column + 1
        gram.append(temp)
        row = row + 1
    print(gram)


    ##Init α Start
    i = 0
    alpha = []
    while(i<dataNum):
        alpha.append(0)
        i=i+1
    ##Init α Done

    haveFailPoint = True;           #标记是否有预测失败的点
    while(haveFailPoint):
        haveFailPoint = False
        i=0
        while(i<dataNum):#循环判断数据集中每个点
            j=0
            calcResult=0;
            while(j<dataNum):#计算误分条件前半部分
                if i>j:
                    calcResult = calcResult+gram[i][j]*alpha[j]*data[j][1]
                else:
                    calcResult = calcResult+gram[j][i]*alpha[j]*data[j][1]
                j = j + 1
                
            calcResult = calcResult + b
            calcResult = calcResult * data[i][1]
            if calcResult <= 0 :#误分
                print("误分点为%d"%(i))
                print(alpha,b)
                alpha[i] = alpha[i] + 1
                b = b + data[i][1]
                haveFailPoint = True
                break
            else:
                i = i+1

    print(alpha,b)
#PerceptronV2 End 
PerceptronV2(GetInput())